﻿using System;

using System.Collections.Generic;
using System.Text;
using Silvermoon.Controls.Classes;
using System.Drawing;
using Silvermoon.Controls.Interfaces;
using Silvermoon.Animations;
using Silvermoon.Controls.Base;
using System.Diagnostics;
using Silvermoon.Shapes;

namespace Silvermoon.Controls
{
    /// <summary>
    /// Panel that contains collections of controls and provides a vertical scrollbar if the content does not fit into the available region.
    /// Consider to use <see cref="ListBox"/> instead if you have plenty of controls to view since ScrollPanel does not disable controls which
    /// are not in view, but ListBox does.
    /// </summary>
    public class ScrollPanel : ScrollViewer, ICompositeControl
    {
        #region classes

        class ScrollContainer : Panel, INotifyControlCollection
        {
            protected ScrollPanel Panel
            {
                get { return Parent as ScrollPanel; }
            }

            protected override Shape CreateDefaultBackground()
            {
                return null;
            }

            //void OnControlBoundsChanged(object sender, EventArgs e)
            //{
            //    OnControlCollectionChanged();
            //}

            protected override void OnChildSizeChanged(Control child, Size oldSize)
            {
                OnControlCollectionChanged();
            }

            protected override void OnChildLocationChanged(Control child)
            {
                OnControlCollectionChanged();
            }

            void OnControlCollectionChanged()
            {
                ScrollPanel panel = Panel;
                if (panel != null) panel.OnControlsChanged();
            }

            public override void BringIntoView(Rectangle bounds, bool animated)
            {

                if (Parent != null) Parent.BringIntoView(bounds, animated);
            }


            #region INotifyControlCollection Members

            void INotifyControlCollection.ControlAdded(Control c)
            {
                //c.SizeChanged += new EventHandler(OnControlBoundsChanged);
                //c.LocationChanged += new EventHandler(OnControlBoundsChanged);
                OnControlCollectionChanged();
            }

            void INotifyControlCollection.CollectionCleared()
            {
                OnControlCollectionChanged();
            }

            void INotifyControlCollection.ControlRemoved(Control c)
            {
                //c.SizeChanged -= new EventHandler(OnControlBoundsChanged);
                //c.LocationChanged -= new EventHandler(OnControlBoundsChanged);
                OnControlCollectionChanged();
            }

            #endregion
        }

        #endregion
        const int HideScrollbarDelayValue = 1200;
        #region fields

        private ScrollContainer container;
        private int? virtualHeight;

        #endregion
        #region ctor

        public ScrollPanel()
            : base()
        {
            container = new ScrollContainer();
            VisibleControls.AddFirst(container);
            ControlFlags |= ControlFlags.CanPan;
        }


        #endregion
        #region props

        public bool IsFocusable
        {
            get { return container.IsFocusable; }
            set { container.IsFocusable = value; }
        }

        /// <summary>
        /// Gets the colleciton of controls for the panel.
        /// </summary>
        public ControlCollection Controls { get { return container.Controls; } }


        /// <summary>
        /// Gets the height of the content. If this value is larger than the control's height, a scrollbar becomes available.
        /// </summary>
        /// <value></value>
        public override int VirtualHeight
        {
            get
            {
                if (virtualHeight == null) virtualHeight = GetTotalHeight();
                return virtualHeight.Value;
            }
        }


        #endregion
        #region methods

        protected override void PreLayout()
        {
            if (CheckFlag(InvalidationFlag.Size | InvalidationFlag.Layout | InvalidationFlag.Height))
            {
                container.Bounds = new Rectangle(0, -yOffset, Width, VirtualHeight);
            }
            base.PreLayout();
        }

        /// <summary>
        /// Changing the bounds of the container might cause child controls to recalculate it's size and invalidate the virtual height again.
        /// therefore the resulting height is determined in the Loaded methods.
        /// </summary>
        protected override void Loaded()
        {
            base.Loaded();
            if (CheckFlag(InvalidationFlag.Size | InvalidationFlag.Layout | InvalidationFlag.Height))
            {
                container.Bounds = new Rectangle(0, -yOffset, Width, VirtualHeight);
            }
            //y<container.Bounds = new Rectangle(0, -yOffset, Width, VirtualHeight);
        }

        protected virtual int GetTotalHeight()
        {
            int total = 0;
            Control c = Controls.First;
            while (c != null)
            {
                total = Math.Max(total, c.Bottom);
                c = c.NextControl;
            }
            return total;
        }

        protected override void OnSizeChanged()
        {
            base.OnSizeChanged();
            EnsureRestState(IsInitialized, PowerEase.Out);
        }

        protected override void OnInitialized()
        {
            base.OnInitialized();
            Invalidate(InvalidationFlag.Layout);
        }

        internal void OnControlsChanged()
        {
            virtualHeight = null;
            EnsureRestState(IsInitialized, PowerEase.Out);
            Invalidate(InvalidationFlag.Layout | InvalidationFlag.Size);

        }

        public void InvalidateVirtualHeight()
        {
            OnControlsChanged();
        }

        #endregion
    }
}
